package com.autumn.log.service.domain.impl;

import com.autumn.common.api.application.dto.output.IPageQueryResult;
import com.autumn.common.api.application.dto.output.PageQueryResult;
import com.autumn.log.LogEventData;
import com.autumn.log.LogEventDataMessage;
import com.autumn.log.service.domain.LogBiz;
import com.autumn.log.service.domain.entities.ServiceLogData;
import com.autumn.log.service.dto.input.ServiceLogDataDto;
import com.autumn.log.service.dto.input.ServiceLogDataQueryInput;
import com.autumn.util.ExceptionUtils;
import com.autumn.util.StringUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

@Service
public class LogBizImpl implements LogBiz {

    private final Map<String, String> collection_Keys = new ConcurrentHashMap<>();

    @Autowired
    private MongoTemplate template;

    private final static String COLLECTION_PREFIX = "Log";

    private List<ServiceLogDataDto> logDataList(LogEventData logData) {
        List<ServiceLogDataDto> items = new ArrayList<>();
        if (logData != null && !StringUtils.isNullOrBlank(logData.getServiceId()) && logData.getMessages() != null) {
            for (LogEventDataMessage msg : logData.getMessages()) {
                if (msg != null) {
                    ServiceLogDataDto log = new ServiceLogDataDto();
                    log.setClientAddress(msg.getClientAddress());
                    log.setIdentity(msg.getIdentity());
                    if (msg.getLevel() != null) {
                        log.setLevel(msg.getLevel().toUpperCase());
                    } else {
                        log.setLevel("INFO");
                    }
                    log.setLoggerName(msg.getLoggerName());
                    if (msg.getServiceLogTime() != null) {
                        log.setLogTime(new Date(msg.getServiceLogTime()));
                    }
                    log.setMessage(msg.getMessage());
                    log.setPid(msg.getPid());
                    log.setServiceAddress(logData.getServiceAddress());
                    log.setServiceId(logData.getServiceId().trim());
                    log.setThreadName(msg.getThreadName());
                    log.setUserName(msg.getUserName());

                    items.add(log);
                }
            }
        }
        return items;
    }

    private String getCollectionName(String serviceId) {
        return String.format("%s_%s", COLLECTION_PREFIX, serviceId.trim()).toLowerCase();
    }

    @Override
    public void save(LogEventData logData) {
        List<ServiceLogDataDto> items = logDataList(logData);
        if (items.size() > 0) {
            String collectionName = this.getCollectionName(logData.getServiceId());
            String value = collection_Keys.get(collectionName);
            boolean isInit = false;
            if (value == null) {
                if (template.collectionExists(collectionName)) {
                    collection_Keys.put(collectionName, collectionName);
                } else {
                    isInit = true;
                }
            }
            template.insert(items, collectionName);
            if (isInit) {
                collection_Keys.put(collectionName, collectionName);
                DBCollection db = template.getCollection(collectionName);
                db.createIndex("serviceAddress");
                db.createIndex("clientAddress");
                db.createIndex("logTime");
                db.createIndex("level");
                db.createIndex("loggerName");
                db.createIndex(new BasicDBObject("message", "text"));
            }
        }
    }

    private final static int MIN_CURRENT_PAGE = 1;
    private final static int MIN_PAGE_SIZE = 10;
    private final static int MAX_PAGE_SIZE = 200;

    private void check(ServiceLogDataQueryInput input) {
        ExceptionUtils.checkNotNull(input, "input");
        if (StringUtils.isNullOrBlank(input.getServiceId())) {
            ExceptionUtils.throwConfigureException("服务id不能为空");
        }
        if (input.getCurrentPage() < MIN_CURRENT_PAGE) {
            input.setCurrentPage(MIN_CURRENT_PAGE);
        }
        if (input.getPageSize() < MIN_PAGE_SIZE) {
            input.setPageSize(MIN_PAGE_SIZE);
        } else {
            if (input.getPageSize() > MAX_PAGE_SIZE) {
                input.setPageSize(MAX_PAGE_SIZE);
            }
        }
        if (input.getLogBeginTime() != null && input.getLogEndTime() != null) {
            if (input.getLogBeginTime().getTime() > input.getLogEndTime().getTime()) {
                ExceptionUtils.throwConfigureException("开始时间不能大于结束时间");
            }
        }
    }

    /*
     * private Criteria createCriteria(Criteria left, Criteria rigth) { if (left !=
     * null) { return left.andOperator(rigth); } else { return rigth; } }
     */

    private List<Criteria> createCriterias(ServiceLogDataQueryInput input) {
        List<Criteria> criterias = new ArrayList<>();
        if (!StringUtils.isNullOrBlank(input.getLevel())) {
            criterias.add(Criteria.where("level").is(input.getLevel().trim().toUpperCase()));
        }
        if (!StringUtils.isNullOrBlank(input.getServiceAddress())) {
            criterias.add(Criteria.where("serviceAddress").is(input.getServiceAddress().trim()));
        }
        if (!StringUtils.isNullOrBlank(input.getClientAddress())) {
            criterias.add(Criteria.where("clientAddress").is(input.getClientAddress().trim()));
        }
        if (!StringUtils.isNullOrBlank(input.getIdentity())) {
            criterias.add(Criteria.where("identity").is(input.getIdentity().trim()));
        }
        if (!StringUtils.isNullOrBlank(input.getUserName())) {
            criterias.add(Criteria.where("userName").is(input.getUserName().trim()));
        }
        if (input.getLogBeginTime() != null && input.getLogEndTime() != null) {
            criterias.add(Criteria.where("logTime").gte(input.getLogBeginTime()).lte(input.getLogEndTime()));
        } else {
            if (input.getLogBeginTime() != null) {
                criterias.add(Criteria.where("logTime").gte(input.getLogBeginTime()));
            }
            if (input.getLogEndTime() != null) {
                criterias.add(Criteria.where("logTime").lte(input.getLogEndTime()));
            }
        }
        if (!StringUtils.isNullOrBlank(input.getLoggerName())) {
            Pattern pattern = Pattern.compile("^" + input.getLoggerName() + ".*$", Pattern.CASE_INSENSITIVE);
            criterias.add(Criteria.where("loggerName").regex(pattern));
        }
        if (!StringUtils.isNullOrBlank(input.getMessage())) {
            Pattern pattern = Pattern.compile("^.*" + input.getMessage() + ".*$", Pattern.CASE_INSENSITIVE);
            criterias.add(Criteria.where("message").regex(pattern));
        }
        return criterias;
    }

    @Override
    public IPageQueryResult<ServiceLogData> queryByPage(ServiceLogDataQueryInput input) {
        this.check(input);
        String collectionName = this.getCollectionName(input.getServiceId());
        PageQueryResult<ServiceLogData> result;
        if (!template.collectionExists(collectionName)) {
            result = new PageQueryResult<>(input.getCurrentPage(), input.getPageSize(), 0);
            return result;
        }
        List<Criteria> criterias = createCriterias(input);
        Query query = new Query();
        for (Criteria criteria : criterias) {
            query.addCriteria(criteria);
        }
        long count = template.count(query, collectionName);
        result = new PageQueryResult<>(input.getCurrentPage(), input.getPageSize(), (int) count);
        int skip = (result.getCurrentPage() - 1) * result.getPageSize();
        query = query.skip(skip).limit(result.getPageSize()).with(new Sort(new Sort.Order(Sort.Direction.ASC, "logTime")));
        List<ServiceLogData> items = template.find(query, ServiceLogData.class, collectionName);
        result.setItems(items);
        return result;
    }
}
